View Javadoc
1   package org.apache.maven.surefire.junitcore;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Map;
23  import org.apache.maven.surefire.report.ConsoleOutputReceiver;
24  import org.apache.maven.surefire.report.ConsoleStream;
25  import org.apache.maven.surefire.report.ReportEntry;
26  import org.apache.maven.surefire.report.ReporterFactory;
27  import org.apache.maven.surefire.report.RunListener;
28  import org.apache.maven.surefire.report.StackTraceWriter;
29  import org.apache.maven.surefire.testset.TestSetFailedException;
30  
31  import static org.apache.maven.surefire.junitcore.TestMethod.getThreadTestMethod;
32  
33  /**
34   * Handles responses from concurrent junit
35   * <p/>
36   * Stuff to remember about JUnit threading:
37   * parallel=classes; beforeClass/afterClass, constructor and all tests method run on same thread
38   * parallel=methods; beforeClass/afterClass run on main thread, constructor + each test method run on same thread
39   * parallel=both; same as parallel=methods
40   *
41   * @see org.apache.maven.surefire.junitcore.JUnitCoreRunListener for details about regular junit run listening
42   * @author Kristian Rosenvold
43   */
44  public abstract class ConcurrentRunListener
45      implements RunListener, ConsoleOutputReceiver
46  {
47      private final Map<String, TestSet> classMethodCounts;
48  
49      private final ThreadLocal<RunListener> reporterManagerThreadLocal;
50  
51      private final boolean reportImmediately;
52  
53      private final ConsoleStream consoleStream;
54  
55      ConcurrentRunListener( final ReporterFactory reporterFactory, ConsoleStream consoleStream,
56                             boolean reportImmediately, Map<String, TestSet> classMethodCounts )
57          throws TestSetFailedException
58      {
59          this.reportImmediately = reportImmediately;
60          this.classMethodCounts = classMethodCounts;
61          this.consoleStream = consoleStream;
62          reporterManagerThreadLocal = new ThreadLocal<RunListener>()
63          {
64              @Override
65              protected RunListener initialValue()
66              {
67                  return reporterFactory.createReporter();
68              }
69          };
70      }
71  
72      public void testSetStarting( ReportEntry description )
73      {
74      }
75  
76      public void testSetCompleted( ReportEntry result )
77      {
78          final RunListener reporterManager = getRunListener();
79          for ( TestSet testSet : classMethodCounts.values() )
80          {
81              testSet.replay( reporterManager );
82          }
83          reporterManagerThreadLocal.remove();
84      }
85  
86      public void testFailed( ReportEntry failure )
87      {
88          final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
89          if ( testMethod != null )
90          {
91              testMethod.testFailure( failure );
92              testMethod.detachFromCurrentThread();
93          }
94      }
95  
96      public void testError( ReportEntry failure )
97      {
98          final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
99          if ( testMethod != null )
100         {
101             testMethod.testError( failure );
102             testMethod.detachFromCurrentThread();
103         }
104     }
105 
106     public void testSkipped( ReportEntry description )
107     {
108         TestSet testSet = getTestSet( description );
109         TestMethod testMethod = testSet.createThreadAttachedTestMethod( description );
110         testMethod.testIgnored( description );
111         testSet.incrementFinishedTests( getRunListener(), reportImmediately );
112         testMethod.detachFromCurrentThread();
113     }
114 
115     public void testExecutionSkippedByUser()
116     {
117         // cannot guarantee proper call to all listeners
118         reporterManagerThreadLocal.get().testExecutionSkippedByUser();
119     }
120 
121     public void testAssumptionFailure( ReportEntry failure )
122     {
123         final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
124         if ( testMethod != null )
125         {
126             testMethod.testIgnored( failure );
127             testMethod.detachFromCurrentThread();
128         }
129     }
130 
131     public void testStarting( ReportEntry description )
132     {
133         TestSet testSet = getTestSet( description );
134         testSet.createThreadAttachedTestMethod( description );
135 
136         checkIfTestSetCanBeReported( testSet );
137         testSet.attachToThread();
138     }
139 
140     public void testSucceeded( ReportEntry report )
141     {
142         TestMethod testMethod = getThreadTestMethod();
143         if ( testMethod != null )
144         {
145             testMethod.testFinished();
146             testMethod.getTestSet().incrementFinishedTests( getRunListener(), reportImmediately );
147             testMethod.detachFromCurrentThread();
148         }
149     }
150 
151     private TestMethod getOrCreateThreadAttachedTestMethod( ReportEntry description )
152     {
153         TestMethod threadTestMethod = getThreadTestMethod();
154         if ( threadTestMethod != null )
155         {
156             return threadTestMethod;
157         }
158         TestSet testSet = getTestSet( description );
159         if ( testSet == null )
160         {
161             consoleStream.println( description.getName() );
162             StackTraceWriter writer = description.getStackTraceWriter();
163             if ( writer != null )
164             {
165                 consoleStream.println( writer.writeTraceToString() );
166             }
167             return null;
168         }
169         else
170         {
171             return testSet.createThreadAttachedTestMethod( description );
172         }
173     }
174 
175     protected abstract void checkIfTestSetCanBeReported( TestSet testSetForTest );
176 
177     TestSet getTestSet( ReportEntry description )
178     {
179         return classMethodCounts.get( description.getSourceName() );
180     }
181 
182     RunListener getRunListener()
183     {
184         return reporterManagerThreadLocal.get();
185     }
186 
187     public static ConcurrentRunListener createInstance( Map<String, TestSet> classMethodCounts,
188                                                             ReporterFactory reporterFactory,
189                                                             boolean parallelClasses, boolean parallelBoth,
190                                                             ConsoleStream consoleStream )
191         throws TestSetFailedException
192     {
193         return parallelClasses
194             ? new ClassesParallelRunListener( classMethodCounts, reporterFactory, consoleStream )
195             : new MethodsParallelRunListener( classMethodCounts, reporterFactory, !parallelBoth, consoleStream );
196     }
197 
198 
199     public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
200     {
201         TestMethod threadTestMethod = getThreadTestMethod();
202         if ( threadTestMethod != null )
203         {
204             LogicalStream logicalStream = threadTestMethod.getLogicalStream();
205             logicalStream.write( stdout, buf, off, len );
206         }
207         else
208         {
209             // Not able to associate output with any thread. Just dump to console
210             consoleStream.println( buf, off, len );
211         }
212     }
213 }